/*
 * Lucas McDaniel
 * GSoC '11
 * HoneyViz
 * 
 * DataSets contain links to Events based off a specific set of criteria.  The 
 * purpose of this class is to allow data queries to happen faster by combining 
 * the predicted results of these queries into groups.  The current build 
 * contains 3 types of DataSets:  event name, time, and location.  This way 
 * queries for a single type of event, date, or location can happen with a 
 * single lookup.
 * 
 * The query process for this class starts with either the search or groupby 
 * methods (where groupby is simply a wrapper around search).  The search 
 * criteria is then passed to the optimizer which determines the type of 
 * DataSet is being used as and if to select all, none, or part of the data in 
 * this bucket.  These events are then limited by the selected regions (made 
 * by selecting an area on the map) and then grouped together based off of the
 * selected criteria (if applicable).  The results are returned to HoneyData.
 */

import java.sql.Time;
import org.w3c.dom.*;
import java.util.*;

public class DataSet {
    
    public ArrayList list;
    
    public String name;
    public Date time;
    public double longitude;
    public double latitude;
    
    public DataSet()
    {
        list = new ArrayList();
        name = null;
        time = null;
        longitude = 0;
        latitude = 0;
    }
    
    public DataSet(String name)
    {
        this();
        this.name = name;
    }
    
    public DataSet(Date time)
    {
        this();
        this.time = time;
    }
    
    public DataSet(double latitude, double longitude)
    {
        this();
        this.longitude = longitude;
        this.latitude = latitude;
    }
    
    public void addNew(DataElement DE)
    {
        if (list.size()==0)
            list.add(DE);
        else
        {
            int indexVal = getIndex(0, list.size()-1, DE);
            if (((DataElement)list.get(indexVal)).compareTo(DE)>0)
                list.add(indexVal+1, DE);
            else
                list.add(indexVal, DE);
        }
    }
    
    private int getIndex(int first, int last, DataElement DE)
    {
        int mid = (first + last)/2;
        int cmpVal = compareTo((DataElement)list.get(mid), DE);
        if (cmpVal == 0 || first >= last)
            return mid;
        else if (cmpVal < 0)
            return getIndex(mid+1, last, DE);
        else
            return getIndex(first, mid-1, DE);
    }
    
    private int compareTo(DataElement e1, DataElement e2)
    {
        int nameVal = e1.name.compareTo(e2.name);
        if (nameVal != 0)
            return nameVal;
        int dateVal = e1.time.compareTo(e2.time);
        if (dateVal != 0)
            return dateVal;
        double longVal = e1.latitude - e2.latitude;
        if (longVal != 0)
            return longVal>0?1:-1;
        return e1.longitude > e2.longitude?1:-1;
    }

    private String getDataElement(Element e, String val)
    {
        return e.getElementsByTagName(val).item(0).getChildNodes().item(0).getNodeValue();
    }
    
    public ArrayList get(String[] names, Date[] dates, double[][] location)
    {
        ArrayList rtnVal = optimizer(names, dates, location);
        if (AppletState.getTopX() !=-1 && AppletState.getBotX() != -1 || AppletState.getSelectedLocations()!=null)
            rtnVal = getSelected(rtnVal);
        return rtnVal;
    }
    
    private ArrayList getSelected(ArrayList list)
    {
        ArrayList rtnVal = new ArrayList();
        double topX = (AppletState.getTopX()/(double)AppletState.getSizeX()-0.5)*360.0;
        double botY = (AppletState.getTopY()/(double)AppletState.getSizeY()-0.48)*-180.0;
        double botX = (AppletState.getBotX()/(double)AppletState.getSizeX()-0.5)*360.0;
        double topY = (AppletState.getBotY()/(double)AppletState.getSizeY()-0.48)*-180.0;
        for (int inc=0; inc<list.size(); inc++)
        {
            DataElement DE = (DataElement)list.get(inc);
            double x = DE.longitude;
            double y = DE.latitude;
            if (AppletState.getSelectedLocations()!=null)
            {
                double[][] tmpArr = AppletState.getSelectedLocations();
                for (int inc2=0; inc2<tmpArr.length; inc2++)
                    if (x == tmpArr[inc2][1] && y == tmpArr[inc2][0])
                        rtnVal.add(DE);
            }
            else if (topX <= x && botX >= x && topY <= y && botY >= y)
                    rtnVal.add(DE);
        }
        return rtnVal;
    }
    
    private ArrayList optimizer(String[] names, Date[] dates, double[][] location)
    {
        ArrayList copy = (ArrayList)list.clone();
        ArrayList rtnVal = new ArrayList();
        if (name!=null && longitude == 0 && latitude == 0 && time == null && names!=null && dates == null && location == null)
        {
            boolean found = false;
            for (int inc=0; inc<names.length; inc++)
                if (names[inc].compareTo(name)==0)
                    found = true;
            if (!found)
                return new ArrayList();
            else
                return list;
        }
        else if (name==null && longitude == 0 && latitude == 0 && time != null && names==null && dates != null && location == null)
        {
            boolean found = false;
            for (int inc=0; inc<names.length; inc++)
                if (dates[inc].compareTo(time)==0)
                    found = true;
            if (!found)
                return new ArrayList();
            else
                return list;
        }
        else if (name==null && longitude != 0 && latitude != 0 && time == null && names==null && dates == null && location != null)
        {
            boolean found = false;
            for (int inc=0; inc<location.length; inc++)
                if (location[inc][0] == latitude && location[inc][1] == longitude)
                    found = true;
            if (!found)
                return new ArrayList();
            else
                return list;
        }
        else
        {
            if (copy.size()==0)
                return new ArrayList();
            if (names == null)
            {
                names = new String[1];
                names[0] = null;
            }
            if (dates == null)
            {
                dates = new Date[1];
                dates[0] = null;
            }
            if (location == null)
            {
                location = new double[1][2];
                location[0][0] = 0;
                location[0][1] = 0;
            }
            for (int inc1=0; inc1<names.length; inc1++)
                for (int inc2=0; inc2<dates.length; inc2++)
                    for (int inc3=0; inc3<location.length; inc3++)
                    {
                        int compare = 1;
                        if (copy.size() != 0)
                        {
                            for (int inc=0; inc<copy.size(); inc++)
                            {
                                compare = ((DataElement)copy.get(inc)).isInSearchParameters(names[inc1], dates[inc2], location[inc3][0], location[inc3][1]);
                                if (compare == 0)
                                {
                                    rtnVal.add(copy.remove(inc));
                                    
                                }
                                
                            }
/*                            do
                            {
                                compare = ((DataElement)copy.get(0)).isInSearchParameters(names[inc1], dates[inc2], location[inc3][0], location[inc3][1]);
                                if (compare == 0)
                                    rtnVal.add(copy.remove(0));
                                else
                                    copy.remove(0);
                            } while (copy.size()!=0);
*/                        }
                    }
        }
        return rtnVal;
    }
    
    public ArrayList groupBy(String[] names, Date[] dates, double[][]location, boolean groupName, boolean groupDates, boolean grouplocation)
    {
        ArrayList rtnVal = new ArrayList();
        ArrayList tmpList = get(names, dates, location);
        if (tmpList.size()!=0)
        {
            for (int inc=0; inc<tmpList.size(); inc++)
            {
                boolean found = false;
                DataElement DE = (DataElement)tmpList.get(inc);
                for (int inc2=0; inc2<rtnVal.size(); inc2++)
                {
                    DataElement DE2 = (DataElement)rtnVal.get(inc2);
                    if ((!groupName || DE.name.compareTo(DE2.name)==0) && (!groupDates || DE.time.compareTo(DE2.time)==0) && ((!grouplocation || DE.latitude==DE2.latitude) && (!grouplocation || DE.longitude==DE2.longitude)))
                    {
                        DE2.count += AppletState.isDistinct()?1:DE.count;
                        found = true;
                        break;
                    }
                }
                if (!found)
                    rtnVal.add(new DataElement(groupName?DE.name:"Group", groupDates?DE.time:new Time(0), AppletState.isDistinct()?1:DE.count, grouplocation?DE.latitude:0, grouplocation?DE.longitude:0));
            }
        }
        return rtnVal;
    }
}

